package org.zirco.sync;

import java.io.IOException;
import java.net.URI;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.emergent.android.weave.client.QueryParams;
import org.emergent.android.weave.client.QueryResult;
import org.emergent.android.weave.client.UserWeave;
import org.emergent.android.weave.client.WeaveAccountInfo;
import org.emergent.android.weave.client.WeaveBasicObject;
import org.emergent.android.weave.client.WeaveException;
import org.emergent.android.weave.client.WeaveFactory;
import org.json.JSONException;
import org.json.JSONObject;
import org.zirco.providers.BookmarksProviderWrapper;
import org.zirco.providers.WeaveColumns;
import org.zirco.utils.Constants;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.os.AsyncTask;
import android.preference.PreferenceManager;

public class WeaveSyncTask extends AsyncTask<WeaveAccountInfo, Integer, Throwable> {

    private static final String WEAVE_PATH            = "/storage/bookmarks";

    private static final String WEAVE_HEADER_TYPE     = "type";

    private static final String WEAVE_VALUE_BOOKMARK  = "bookmark";
    private static final String WEAVE_VALUE_FOLDER    = "folder";
    private static final String WEAVE_VALUE_ITEM      = "item";
    private static final String WEAVE_VALUE_ID        = "id";
    private static final String WEAVE_VALUE_PARENT_ID = "parentid";
    private static final String WEAVE_VALUE_TITLE     = "title";
    private static final String WEAVE_VALUE_URI       = "bmkUri";
    private static final String WEAVE_VALUE_DELETED   = "deleted";

    private static WeaveFactory mWeaveFactory         = null;

    private static WeaveFactory getWeaveFactory() {
        if (mWeaveFactory == null) {
            mWeaveFactory = new WeaveFactory(true);
        }

        return mWeaveFactory;
    }

    private Context         mContext;
    private ContentResolver mContentResolver;
    private ISyncListener   mListener;

    private boolean         mFullSync = false;

    public WeaveSyncTask(Context context, ISyncListener listener){
        mContext = context;
        mContentResolver = context.getContentResolver();
        mListener = listener;
    }

    @Override
    protected Throwable doInBackground(WeaveAccountInfo... arg0) {
        Throwable result = null;

        try {
            publishProgress(0, 0, 0);

            WeaveAccountInfo accountInfo = arg0[0];
            UserWeave userWeave = getWeaveFactory().createUserWeave(accountInfo.getServer(), accountInfo.getUsername(),
                                                                    accountInfo.getPassword());

            long lastModifiedDate = getLastModified(userWeave).getTime();
            long lastSyncDate = PreferenceManager.getDefaultSharedPreferences(mContext).getLong(Constants.PREFERENCE_WEAVE_LAST_SYNC_DATE,
                                                                                                -1);

            if (lastModifiedDate > lastSyncDate) {

                publishProgress(1, 0, 0);

                mFullSync = lastSyncDate <= 0;

                QueryResult<List<WeaveBasicObject>> queryResult;

                QueryParams parms = null;
                if (!mFullSync) {
                    parms = new QueryParams();
                    parms.setFull(false);
                    parms.setNewer(new Date(lastSyncDate));
                } else {
                    BookmarksProviderWrapper.clearWeaveBookmarks(mContentResolver);
                }

                queryResult = getCollection(userWeave, WEAVE_PATH, parms);
                List<WeaveBasicObject> wboList = queryResult.getValue();

                if (mFullSync) {
                    doSync(accountInfo, userWeave, wboList);
                } else {
                    doSyncByDelta(accountInfo, userWeave, wboList);
                }
            }

        } catch (WeaveException e) {
            e.printStackTrace();
            result = e;
        } catch (JSONException e) {
            e.printStackTrace();
            result = e;
        } catch (IOException e) {
            e.printStackTrace();
            result = e;
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
            result = e;
        }

        return result;
    }

    private void doSync(WeaveAccountInfo accountInfo, UserWeave userWeave, List<WeaveBasicObject> wboList)
                                                                                                          throws WeaveException,
                                                                                                          JSONException,
                                                                                                          IOException,
                                                                                                          GeneralSecurityException {

        int i = 0;
        int count = wboList.size();

        List<ContentValues> values = new ArrayList<ContentValues>();

        mContext.getContentResolver().delete(WeaveColumns.CONTENT_URI, null, null);

        for (WeaveBasicObject wbo : wboList) {
            JSONObject decryptedPayload = wbo.getEncryptedPayload(userWeave, accountInfo.getSecret());

            i++;

            // if (i > 30) break;
            // Log.d("Decrypted:", decryptedPayload.toString());

            if (decryptedPayload.has(WEAVE_HEADER_TYPE)
                && ((decryptedPayload.getString(WEAVE_HEADER_TYPE).equals(WEAVE_VALUE_BOOKMARK)) || (decryptedPayload.getString(WEAVE_HEADER_TYPE).equals(WEAVE_VALUE_FOLDER)))) {

                if (decryptedPayload.has(WEAVE_VALUE_TITLE)) {

                    boolean isFolder = decryptedPayload.getString(WEAVE_HEADER_TYPE).equals(WEAVE_VALUE_FOLDER);

                    String title = decryptedPayload.getString(WEAVE_VALUE_TITLE);
                    String weaveId = decryptedPayload.has(WEAVE_VALUE_ID) ? decryptedPayload.getString(WEAVE_VALUE_ID) : null;
                    String parentId = decryptedPayload.has(WEAVE_VALUE_PARENT_ID) ? decryptedPayload.getString(WEAVE_VALUE_PARENT_ID) : null;

                    if ((title != null) && (title.length() > 0)) {

                        ContentValues value = new ContentValues();
                        value.put(WeaveColumns.WEAVE_BOOKMARKS_TITLE, title);
                        value.put(WeaveColumns.WEAVE_BOOKMARKS_WEAVE_ID, weaveId);
                        value.put(WeaveColumns.WEAVE_BOOKMARKS_WEAVE_PARENT_ID, parentId);

                        if (isFolder) {
                            value.put(WeaveColumns.WEAVE_BOOKMARKS_FOLDER, true);
                        } else {
                            String url = decryptedPayload.getString(WEAVE_VALUE_URI);

                            value.put(WeaveColumns.WEAVE_BOOKMARKS_FOLDER, false);
                            value.put(WeaveColumns.WEAVE_BOOKMARKS_URL, url);
                        }

                        values.add(value);
                    }
                }
            }

            publishProgress(2, i, count);

            if (isCancelled()) {
                break;
            }
        }

        int j = 0;
        ContentValues[] valuesArray = new ContentValues[values.size()];
        for (ContentValues value : values) {
            valuesArray[j++] = value;
        }

        publishProgress(3, 0, 0);
        mContext.getContentResolver().bulkInsert(WeaveColumns.CONTENT_URI, valuesArray);
    }

    private void doSyncByDelta(WeaveAccountInfo accountInfo, UserWeave userWeave, List<WeaveBasicObject> wboList)
                                                                                                                 throws WeaveException,
                                                                                                                 JSONException,
                                                                                                                 IOException,
                                                                                                                 GeneralSecurityException {

        int i = 0;
        int count = wboList.size();

        for (WeaveBasicObject wbo : wboList) {
            JSONObject decryptedPayload = wbo.getEncryptedPayload(userWeave, accountInfo.getSecret());

            i++;

            if (decryptedPayload.has(WEAVE_HEADER_TYPE)) {

                if (decryptedPayload.getString(WEAVE_HEADER_TYPE).equals(WEAVE_VALUE_ITEM)
                    && decryptedPayload.has(WEAVE_VALUE_DELETED) && decryptedPayload.getBoolean(WEAVE_VALUE_DELETED)) {

                    String weaveId = decryptedPayload.has(WEAVE_VALUE_ID) ? decryptedPayload.getString(WEAVE_VALUE_ID) : null;
                    if ((weaveId != null) && (weaveId.length() > 0)) {
                        // mDbAdapter.deleteWeaveBookmarkByWeaveId(weaveId);
                        BookmarksProviderWrapper.deleteWeaveBookmarkByWeaveId(mContentResolver, weaveId);
                    }
                } else if (decryptedPayload.getString(WEAVE_HEADER_TYPE).equals(WEAVE_VALUE_BOOKMARK)
                           || decryptedPayload.getString(WEAVE_HEADER_TYPE).equals(WEAVE_VALUE_FOLDER)) {

                    String weaveId = decryptedPayload.has(WEAVE_VALUE_ID) ? decryptedPayload.getString(WEAVE_VALUE_ID) : null;
                    if ((weaveId != null) && (weaveId.length() > 0)) {

                        boolean isFolder = decryptedPayload.getString(WEAVE_HEADER_TYPE).equals(WEAVE_VALUE_FOLDER);

                        String title = decryptedPayload.getString(WEAVE_VALUE_TITLE);
                        String parentId = decryptedPayload.has(WEAVE_VALUE_PARENT_ID) ? decryptedPayload.getString(WEAVE_VALUE_PARENT_ID) : null;

                        ContentValues values = new ContentValues();
                        values.put(WeaveColumns.WEAVE_BOOKMARKS_WEAVE_ID, weaveId);
                        values.put(WeaveColumns.WEAVE_BOOKMARKS_WEAVE_PARENT_ID, parentId);
                        values.put(WeaveColumns.WEAVE_BOOKMARKS_TITLE, title);

                        if (isFolder) {
                            values.put(WeaveColumns.WEAVE_BOOKMARKS_FOLDER, true);
                        } else {
                            String url = decryptedPayload.getString(WEAVE_VALUE_URI);

                            values.put(WeaveColumns.WEAVE_BOOKMARKS_FOLDER, false);
                            values.put(WeaveColumns.WEAVE_BOOKMARKS_URL, url);
                        }

                        long id = BookmarksProviderWrapper.getWeaveBookmarkIdByWeaveId(mContentResolver, weaveId);// mDbAdapter.getIdByWeaveId(weaveId);
                        if (id == -1) {
                            // Insert.
                            BookmarksProviderWrapper.insertWeaveBookmark(mContentResolver, values);
                        } else {
                            // Update.
                            BookmarksProviderWrapper.updateWeaveBookmark(mContentResolver, id, values);
                        }

                    }
                }
            }

            // Log.d("Decrypted:", decryptedPayload.toString());

            publishProgress(2, i, count);

            if (isCancelled()) {
                break;
            }
        }
    }

    @Override
    protected void onCancelled() {
        mListener.onSyncCancelled();
        super.onCancelled();
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        mListener.onSyncProgress(values[0], values[1], values[2]);
    }

    @Override
    protected void onPostExecute(Throwable result) {
        mListener.onSyncEnd(result);
    }

    private QueryResult<List<WeaveBasicObject>> getCollection(UserWeave weave, String name, QueryParams params)
                                                                                                               throws WeaveException {
        if (params == null) params = new QueryParams();
        URI uri = weave.buildSyncUriFromSubpath(name + params.toQueryString());
        return weave.getWboCollection(uri);
    }

    private Date getLastModified(UserWeave userWeave) throws WeaveException {
        try {
            JSONObject infoCol = userWeave.getNode(UserWeave.HashNode.INFO_COLLECTIONS).getValue();

            if (infoCol.has("bookmarks")) {
                long modLong = infoCol.getLong("bookmarks");
                return new Date(modLong * 1000);
            }

            return null;
        } catch (JSONException e) {
            throw new WeaveException(e);
        }
    }

    public boolean isFullSync() {
        return mFullSync;
    }

}
